#!/usr/bin/env python3
import pandas as panda
tables = panda.read_html("../docs/gbops%20-%20The%20Game%20Boy%20opcode%20table.html")
opcode_table = tables[0] #Selecting the first table (for example)
opcode_table2 = tables[1] #Selecting the first table (for example)
opcode_table=opcode_table.drop(axis=1,labels="--");
opcode_table2=opcode_table2.drop(axis=1,labels="--");

print(opcode_table);
print(opcode_table2);
opcode_list=[]

for r_index, row in opcode_table.iterrows(): #iterate over rows
    for c_index, value in row.items():
        if isinstance(value, float): value="NOP_NO_INSTR 1 1m ----";
        opcode_list+=[value.replace("\u200b","")]

for r_index, row in opcode_table2.iterrows(): #iterate over rows
    for c_index, value in row.items():
        if isinstance(value, float): value="NOP_NO_INSTR 1 1m ----";
        opcode_list+=[value.replace("\u200b","")]
 
print(opcode_list);

unique_ops ={}
unique_src ={}

with open("../src/sb_instr_tables.h","w") as f:
  f.write("""
// This file is autogenerated do not edit

#ifndef SB_INSTR_TABLES
#define SB_INSTR_TABLES 1

#include "sb_instr_impl.h"
#include <stdint.h>

typedef struct{
  sb_opcode_impl_t impl;
  const char* opcode_name;
  uint8_t flag_mask[5];
  uint8_t length; // in bytes
  uint8_t mcycles;
  uint8_t mcycles_branch_taken;
  uint8_t op_src1;
  uint8_t op_src2;
}sb_instr_t;

const static sb_instr_t sb_decode_table[]={
""")
  opcode_num = 0;
  for op in opcode_list:

    # Rename problematic opcodes
    op = op.replace("JP Z,", "JPC Z_FLAG,");
    op = op.replace("JP NZ,","JPC NZ_FLAG,");
    op = op.replace("JP C,", "JPC C_FLAG,");
    op = op.replace("JP NC,","JPC NC_FLAG,");
    
    op = op.replace("JR Z,", "JRC Z_FLAG,");
    op = op.replace("JR NZ,","JRC NZ_FLAG,");
    op = op.replace("JR C,", "JRC C_FLAG,");
    op = op.replace("JR NC,","JRC NC_FLAG,");
     
    op = op.replace("CALL Z,", "CALLC Z_FLAG,");
    op = op.replace("CALL NZ,","CALLC NZ_FLAG,");
    op = op.replace("CALL C,", "CALLC C_FLAG,");
    op = op.replace("CALL NC,","CALLC NC_FLAG,");
    op = op.replace("PREFIX CB","PREFIX");
                              
    op = op.replace("RET Z", "RETC Z_FLAG");
    op = op.replace("RET NZ","RETC NZ_FLAG");
    op = op.replace("RET C", "RETC C_FLAG");
    op = op.replace("RET NC","RETC NC_FLAG");
                                    
                                    
    op_name = op.split(' ')[0];
    
    parameters = op.split(' ')[1].split(',');

    # Handle special case of opcode without arguments
    if len(op.split(' ')) == 4:
        parameters =[]

    while(len(parameters)<2):
        parameters+=["NONE"];

    print(op, parameters)

    unique_ops[op_name] = True;
    splits = op.rsplit(' ', maxsplit=3);
    instr_name ='"'+splits[0]+'"';
    taken_latency = int("0");
    split_mcycles = splits[2].replace('m','').split('-');
    if len(split_mcycles)>1:
       taken_latency= int(split_mcycles[1]);
    non_taken_latency =int( split_mcycles[0]);
    
    if taken_latency < non_taken_latency:
      taken_latency = non_taken_latency;
    length = int(splits[1])
    if opcode_num>255:
       length= length-1;
    opcode_num+=1;

    for i in range(0,len(parameters)):
        p =parameters[i];
        p = p.upper();
        p = p.replace("HL+","HL_INC");
        p = p.replace("HL-","HL_DEC");
        if  p.find("(")!=-1:
            p=p.replace("(","");
            p=p.replace(")","");
            p+= "_INDIRECT";

        p = p.replace("+","_PLUS_")
        parameters[i]= p;
        unique_src[p]=True;

    impl_name = "sb_"+op_name.lower()+"_impl";
    f.write(f'  {{ {impl_name:16}, {instr_name:16}, "{splits[3]}", {length},'
            f'{non_taken_latency}, {taken_latency}, SB_OP_{parameters[0]}, '
            f'SB_OP_{parameters[1]} }},\n') 

  f.write("""
};
#endif
""");


for op in opcode_list:
  unique_ops[op.split(' ')[0]] = True;
  splits = op.rsplit(' ', maxsplit=3);
  #print(splits)

with open("../src/sb_instr_impl_template.h","w") as f:
    f.write("""
// This file is autogenerated do not edit

#ifndef SB_INSTR_IMPL
#define SB_INSTR_IMPL 1

#include "sb_types.h"
#include <stdio.h>
""")
    f.write("\n")
    enum_value = 0
    for (src,_) in sorted(unique_src.items()):
        f.write(f"#define SB_OP_{src} {enum_value}"+'\n');
        enum_value +=1 

    f.write("\n")

                                                                                 
    f.write("uint16_t sb_read16(sb_gb_t *gb, int addr);\n");                                      
    f.write("uint8_t sb_read8(sb_gb_t *gb, int addr);\n\n");                                        
    f.write("void sb_store8(sb_gb_t *gb, int addr, int value);\n\n");                                        
                                         
    f.write("int sb_load_operand(sb_gb_t* gb, int operand){\n")
    f.write("  switch(operand){\n");

    for (src,_) in sorted(unique_src.items()):
        f.write(f"    case SB_OP_{src}: {{ return 0; }}"+'\n');

    f.write("  }\n");
    f.write('  printf("Unhandled operand %d\\n",operand);\n');
    f.write('  return 0;\n');
    f.write("}\n\n");
     
    f.write("void sb_store_operand(sb_gb_t* gb, int operand, int value){\n")
    f.write("  switch(operand){\n");

    for (src,_) in sorted(unique_src.items()):
        f.write(f"    case SB_OP_{src}: {{ return; }}"+'\n');

    f.write("  }\n");
    f.write('  printf("Unhandled operand %d\\n",operand);\n');
    f.write('  return;\n');
    f.write("}\n\n");    

    for (op,_) in sorted(unique_ops.items()):
        f.write("static void sb_"+op.lower()+"_impl(sb_gb_t* gb, int op1, int op2, int op1_enum){\n");
        f.write('  printf("Stubbed opcode '+op+' executed\\n");\n');

        f.write("}\n\n");
    f.write("#endif\n");
